from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext_lazy as _
from django.db import connection, models

def choices_dict(choices):
    """
    takes a choices list and returns the dictionary where the keys are the options
    and the integers are the values
    """
    return dict([(choice[1], choice[0]) for choice in choices])

class MessageManager(models.Manager):
    def send_message(self, to_obj, from_obj, message):
        to_ctype = ContentType.objects.get_for_model(to_obj)
        from_ctype = ContentType.objects.get_for_model(from_obj)
        MessagedItem._default_manager.get_or_create(
            message=message, to_content_type=to_ctype, to_object_id=to_obj.pk,
            from_content_type=from_ctype, from_object_id=from_obj.pk)

    def get_for_object(self, to_obj):
        """
        Create a queryset matching all messages associated with the given
        object.
        """
        to_ctype = ContentType.objects.get_for_model(to_obj)
        return self.filter(messages__to_content_type__pk=to_ctype.pk, messages__to_object_id=to_obj.pk)

    def get_sent_for_object(self, from_obj):
       """
       Create a queryset matching all messages associated with the given
       object.
       """
       from_ctype = ContentType.objects.get_for_model(from_obj)
       return self.filter(messages__from_content_type__pk=from_ctype.pk, messages__from_object_id=from_obj.pk)

class Message(models.Model):
    subject = models.CharField(max_length=200)
    body = models.TextField()

    objects = MessageManager()

    def __unicode__(self):
        return "%s" % self.subject
        
    class Admin:
        pass

class Agent(models.Model):
    content_type = models.ForeignKey(ContentType, verbose_name=_('content type'))
    object_id = models.PositiveIntegerField(_('object id'), db_index=True)
    object = generic.GenericForeignKey('content_type', 'object_id')
    
    class Admin:
        pass

STATUS_CHOICES = ((0, 'unread'),
                  (1, 'read'),
                  (2, 'delete'),
                  )

class MessageItem(models.Model):
    """
    Holds the relationship between a message and the items sending and recieveing a message.
    """
    message = models.ForeignKey(Message, verbose_name=_('message'), related_name='messages')
    status = models.IntegerField(choices=STATUS_CHOICES)

    to_obj = models.ForeignKey(Agent, related_name="senders")
    from_obj = models.ForeignKey(Agent, related_name="recipients")

    class Meta:
        # Enforce unique message association per object
        unique_together = (('message', 'to_obj', 'from_obj'),)
        verbose_name = _('Item Message')
        verbose_name_plural = _('Item Messages')

    class Admin:
        pass

    def __unicode__(self):
        return u'to: %s - from: %s [%s]' % (self.to_object, self.from_object, self.message)
    
    def mark(self, status):
        status_dict = choices_dict(STATUS_CHOICES)
        self.status = status_dict[status]

    def mark_as_read(self):
        self.mark("read")

class PermissionDeniedError(Exception):
    pass

class Queue(models.Model):
    """
    Queue is just a special object to send messages to
    """

    consumers = models.ManyToManyField(Agent, related_name="consumer")
    producers = models.ManyToManyField(Agent, related_name="producer")

    def push(self, from_obj, message):
        if from_obj in self.producers:
            return self.objects.send_message(self, from_obj, message)
        else:
            raise PermissionDeniedError()

    def pop(self, to_obj):
        if to_obj in self.consumers:
            message = self.objects.get_for_object(to_obj)[0]
            message.delete()
            return message
        else:
            raise PermissionDeniedError()

    objects = MessageManager()